home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / du_lib / textedit.c < prev    next >
C/C++ Source or Header  |  1995-07-10  |  32KB  |  1,021 lines

  1. /*
  2.   DU_LIB v2
  3.   Gem Window Management & Dialog Library For Lattice C
  4.   ½1994,1995 by Craig Graham.
  5.  
  6.   Based on the DU_LIBv1 Library for HiSoft Basic.
  7. */
  8.  
  9. /*
  10.     Text Edit Widget
  11.     
  12.     This provides a full text editor with scrolling & mouse click/drag selection
  13.     and GEM clipboard support. If the cut & paste routines look a little inefficient,
  14.     it's because they are intended to allow external access to the cut paste functions
  15.     as well as the standard keyboard shortcuts (^X,^C,^V).
  16. */
  17.  
  18. #include "DULIB.H"
  19. #include <stdio.h>
  20.  
  21. /* Create a text widget */
  22. void Set_text_widget(short dialog, short ob, short max_columns, short max_lines)
  23. {
  24.     text_line *tln_t;
  25.     Elist *n;
  26.  
  27.     Set_object_Kcallback(dialog, ob, &TW_keypress);        // Install Object Layer keyhandler
  28.     Set_object_callback(dialog, ob, &TW_mouse_click);    // Install a callback to handle mouse clicks
  29.     Set_object_redraw(dialog, ob, &TW_display);            // Setup a redraw function
  30.     
  31.     if (find_event(dialog,ob,&n))            // There be an event structure for the widget by now - if there isn't then we've got problems
  32.     {
  33.         n->wtext.max_lines=max_lines;
  34.         n->wtext.max_columns=max_columns;
  35.         n->wtext.current_column=0;
  36.         n->wtext.cursor_y=0;
  37.  
  38.         tln_t=(text_line*)malloc(sizeof(text_line));        // Create initial line
  39.         tln_t->the_text=(char*)malloc(sizeof(char)*max_columns);
  40.         tln_t->the_text[0]='\0';
  41.         tln_t->eof_line=0;                                    // initially nothing on the line
  42.         tln_t->prev=NULL;                                    // No lines before this
  43.         tln_t->next=NULL;                                    // No lines after this
  44.  
  45.         n->wtext.current_line=tln_t;                        // Initial cursor position == top line.
  46.         n->wtext.display_baseline=tln_t;                    // Initial display baseline == top line.
  47.         n->wtext.sel_start_line=n->wtext.sel_end_line=NULL; // Initially, no text selected
  48.         
  49.     }else{
  50.         form_alert(1,"[1][ ERROR: Unable to create text widget. ][ Bugger ]");
  51.     }
  52. }
  53.  
  54. /*
  55.     Change/initialise all the text in a text widget
  56.      This allows you to load text into a text widget.
  57. */
  58. void Change_widget_text(short dialog, short ob, char *t[], short lines)
  59. {
  60.     Elist *n;
  61.     text_line *tln, *tln_t;
  62.     short sl,l,max_columns;
  63.     
  64.     if (find_event(dialog,ob,&n))
  65.     {
  66.         tln=n->wtext.text;                // Dispose of the current text widget contents
  67.         while(tln) {
  68.             tln_t=tln;
  69.             tln=tln->next;
  70.             free(tln_t->the_text);        //free up the char array.
  71.             free(tln_t);                //free up the line structure
  72.         }
  73.         n->wtext.text=NULL;
  74.         n->wtext.current_column=0;
  75.         n->wtext.cursor_y=0;
  76.         n->wtext.sel_start_line=n->wtext.sel_end_line=NULL; // No text selected
  77.         
  78.         if ((lines>n->wtext.max_lines)&&(n->wtext.max_lines>0))
  79.         {
  80.             lines=n->wtext.max_lines;
  81.         }
  82.         
  83.         max_columns=n->wtext.max_columns;
  84.         for (l=0; l<lines; l++)            // Find the maximum number of columns used
  85.         {
  86.             sl=strlen(t[l]);
  87.             if (sl>max_columns) max_columns=sl;
  88.         }
  89.         max_columns++;
  90.         n->wtext.max_columns=max_columns;
  91.         
  92.         tln_t=(text_line*)malloc(sizeof(text_line));        // Create initial line
  93.         tln_t->the_text=(char*)malloc(sizeof(char)*max_columns);
  94.         tln_t->the_text[0]='\0';
  95.         tln_t->eof_line=0;                                    // initially nothing on the line
  96.         tln_t->prev=NULL;                                    // No lines before this
  97.  
  98.         n->wtext.current_line=tln_t;                        // Initial cursor position == top line.
  99.         n->wtext.display_baseline=tln_t;                    // Initial display baseline == top line.
  100.         
  101.         n->wtext.text=tln_t;
  102.         for (l=0; l<lines; l++)
  103.         {
  104.             tln=tln_t;
  105.             
  106.             sprintf(tln->the_text,"%s",t[l]);                // Copy the text into the line entry
  107.             tln->eof_line=strlen(t[l]);
  108.  
  109.             tln_t=(text_line*)malloc(sizeof(text_line));
  110.             tln_t->the_text=(char*)malloc(sizeof(char)*max_columns);
  111.             tln->next=tln_t;
  112.             tln_t->prev=tln;
  113.         }
  114.         free(tln_t);        // Loop creates one to many entries :(
  115.         tln->next=NULL;
  116.         
  117.     }else{
  118.         form_alert(1,"[1][ ERROR: Cann't find text widget info. ][ Bugger ]");
  119.     }
  120.  
  121. }
  122.  
  123. /*
  124.     Get all the text in a text widget
  125.     This will dispose of the current contents of a char *t[] array if text_lines
  126.     is passed 
  127. */
  128. short Get_widget_text(short dialog, short ob, char ***text, short text_lines)
  129. {
  130.     Elist *n;
  131.     text_line *tln;
  132.     char **txt;
  133.     short number_of_lines,l;
  134.     
  135.     if (find_event(dialog,ob,&n))
  136.     {
  137.         if (text_lines)                    // Dispose of current text
  138.         {
  139.             txt=*text;
  140.             for(l=0; l<text_lines; l++) free(txt[l]);
  141.             free(txt);
  142.         }
  143.         
  144.         tln=n->wtext.text;                // Count up the number of lines
  145.         for(number_of_lines=0; tln; number_of_lines++) tln=tln->next;
  146.         
  147.         number_of_lines++;
  148.         
  149.         txt=(char**)malloc(sizeof(char*)*number_of_lines);
  150.         *text=txt;
  151.         
  152.         l=0; tln=n->wtext.text;
  153.         
  154.         do {
  155.             txt[l]=(char*)malloc(sizeof(char)*(tln->eof_line+1));
  156.             
  157.             sprintf(txt[l],"%s",tln->the_text);
  158.             
  159.             l++; tln=tln->next;
  160.         } while (tln);
  161.         
  162.         return number_of_lines;
  163.     }else{
  164.         form_alert(1,"[1][ ERROR: Cann't find text widget info. ][ Bugger ]");
  165.     }
  166. }
  167.  
  168. /*
  169.     Display the text cursor for a given text widget
  170. */
  171. void TW_display_cursor(short dialog, short ob)
  172. {
  173.     Elist *n;
  174.     short ch,cw;
  175.     short pts[8];
  176.     short dlx,dly;
  177.     
  178.     if (find_event(dialog,ob,&n))
  179.     {
  180.         vsf_color(x_handle,1); vsf_interior(x_handle,FIS_SOLID); vsf_perimeter(x_handle,0);
  181.         vswr_mode(x_handle, MD_XOR);
  182.  
  183.         vqt_extent(x_handle,"A",pts);
  184.         ch=abs(pts[1]-pts[7]);                // Height of one character in current font
  185.         cw=abs(pts[0]-pts[2]);                // Width of one character in current font
  186.  
  187.         dlx=cr_clip.g_x+5+cw*n->wtext.current_column; dly=cr_clip.g_y+8+ch*n->wtext.cursor_y;
  188.         pts[0]=dlx; pts[2]=dlx+cw-1;
  189.         pts[1]=dly; pts[3]=dly+ch-1;
  190.         v_bar(x_handle,pts);
  191.         vswr_mode(x_handle, MD_REPLACE);
  192.     }
  193. }
  194.  
  195. /*
  196.     Display a highlighted (ie. selected) region in a text
  197.     widget.
  198. */
  199. void TW_display_highlighted(short dialog, short ob)
  200. {
  201.     Elist *n;
  202.     text_line *tln_t,*tl,*de_l,*ds_l;
  203.     short ch,cw,l;
  204.     short pts[8];
  205.     short dlx,dly;
  206.     short sx, sy, ex, ey;
  207.     
  208.     if (find_event(dialog,ob,&n))
  209.     {
  210.         vsf_color(x_handle,1); vsf_interior(x_handle,FIS_SOLID); vsf_perimeter(x_handle,0);
  211.         vswr_mode(x_handle, MD_XOR);
  212.  
  213.         vqt_extent(x_handle,"A",pts);
  214.         ch=abs(pts[1]-pts[7]);                // Height of one character in current font
  215.         cw=abs(pts[0]-pts[2]);                // Width of one character in current font
  216.  
  217.         tl=n->wtext.display_baseline;
  218.         ds_l=de_l=NULL;
  219.         sy=ey=-1;
  220.         for(l=0; (l<n->wtext.display_lines_high)&&(tl->next!=NULL); tl=tl->next )
  221.         {
  222.             if (tl==n->wtext.sel_start_line)
  223.             {
  224.                 ds_l=tl;
  225.                 sx=n->wtext.sel_start_char;
  226.                 sy=l;
  227.             }
  228.             if (tl==n->wtext.sel_end_line)
  229.             {
  230.                 de_l=tl;
  231.                 ex=n->wtext.sel_end_char;
  232.                 ey=l;
  233.             }
  234.             l++;
  235.         }
  236.         
  237.         if (ds_l==NULL)
  238.         {
  239.             ds_l=n->wtext.display_baseline;
  240.             sx=sy=0;
  241.         }
  242.         if (de_l==NULL)
  243.         {
  244.             de_l=tl;
  245.             ex=tl->eof_line;
  246.             ey=l-1;
  247.         }
  248.         
  249.         if (sy==ey)                            // mark only on a single line
  250.         {
  251.             dlx=cr_clip.g_x+5+cw*sx; dly=cr_clip.g_y+8+ch*sy;
  252.             pts[0]=dlx; pts[2]=dlx+cw*(ex-sx)-1;
  253.             pts[1]=dly; pts[3]=dly+ch-1;
  254.             v_bar(x_handle,pts);
  255.         }else{                                // mark split over two or more lines
  256.             dlx=cr_clip.g_x+5+cw*sx; dly=cr_clip.g_y+8+ch*sy;    // display last line (start_char .. end of line)
  257.             pts[0]=dlx; pts[2]=dlx+cw*(ds_l->eof_line-sx)-1;
  258.             pts[1]=dly; pts[3]=dly+ch-1;
  259.             v_bar(x_handle,pts);
  260.  
  261.             tln_t=ds_l;                    // display middle bit (fully selected lines) - if any
  262.             pts[0]=cr_clip.g_x+5;
  263.             for(l=sy+1; l<ey; l++)
  264.             {
  265.                 tln_t=tln_t->next;
  266.                 pts[2]=pts[0]+cw*tln_t->eof_line-1;
  267.                 pts[1]=cr_clip.g_y+8+ch*l;; pts[3]=pts[1]+ch-1;
  268.                 v_bar(x_handle,pts);
  269.             }
  270.             
  271.             dlx=cr_clip.g_x+5; dly=cr_clip.g_y+8+ch*ey;    // display last line (start of line .. end_char)
  272.             pts[0]=dlx; pts[2]=dlx+cw*ex+1;
  273.             pts[1]=dly; pts[3]=dly+ch-1;
  274.             v_bar(x_handle,pts);
  275.         }
  276.         vswr_mode(x_handle, MD_REPLACE);
  277.     }
  278. }
  279.  
  280. /*
  281.     Text Widget Display Routine
  282. */
  283. short TW_display(void)
  284. {
  285.     Elist *n;
  286.     char cursor[2];
  287.     short de,l,ch,cw;
  288.     short pts[8];
  289.     short dlx,dly,f;
  290.     text_line *tl;
  291.     
  292.     if (find_event(this_dialog,this_ob,&n))
  293.     {
  294.         vst_color(x_handle,1); vst_point(x_handle,10,&junk,&junk,&junk,&junk);
  295.         vst_rotation(x_handle,0); vst_alignment(x_handle,0,0,wm_inv,wm_outv);
  296.         vsf_interior(x_handle,FIS_SOLID); vsf_perimeter(x_handle,0);
  297.         vswr_mode(x_handle, MD_TRANS);
  298.  
  299.         vsf_color(x_handle,0);
  300.         pts[0]=cr_clip.g_x+1; pts[2]=pts[0]+cr_clip.g_w-2; 
  301.         pts[1]=cr_clip.g_y+1; pts[3]=pts[1]+cr_clip.g_h-2;
  302.         v_bar(x_handle,pts);
  303.         vsf_color(x_handle,1);
  304.  
  305.         vqt_extent(x_handle,"A",pts);
  306.         ch=abs(pts[1]-pts[7]);                // Height of one character in current font
  307.         cw=abs(pts[0]-pts[2]);                // Width of one character in current font
  308.         
  309.         cursor[1]='\0';
  310.         
  311.         n->wtext.display_lines_high=(short)(cr_clip.g_h/ch) - 1;    // fill in this each time, just 'coz it's easy to calculate here
  312.  
  313.         de=cr_clip.g_y+cr_clip.g_h-5;
  314.         tl=n->wtext.display_baseline;
  315.         
  316.         if (tl)
  317.         {
  318.             dlx=cr_clip.g_x+5; l=0;
  319.             for(dly=cr_clip.g_y+ch+5; (dly<de)&&(tl!=NULL); dly=dly+ch)
  320.             {
  321.                 v_gtext(x_handle, dlx, dly, tl->the_text);                        // display the text line
  322.                 tl=tl->next;
  323.             }
  324.             vswr_mode(x_handle, MD_XOR);
  325.             dlx=cr_clip.g_x+5+cw*n->wtext.current_column; dly=cr_clip.g_y+8+ch*n->wtext.cursor_y;
  326.             pts[0]=dlx; pts[2]=dlx+cw-1;
  327.             pts[1]=dly; pts[3]=dly+ch-1;
  328.             v_bar(x_handle,pts);
  329.             vswr_mode(x_handle, MD_REPLACE);
  330.         }
  331.         
  332.     /* Highlight any selected text which is currently displayed */
  333.         if ((n->wtext.sel_start_line!=NULL)&&(n->wtext.sel_end_line!=NULL))
  334.         {
  335.             if ((n->wtext.sel_start_line!=n->wtext.sel_end_line)||(n->wtext.sel_start_char!=n->wtext.sel_end_char))
  336.             {
  337.                 tl=n->wtext.display_baseline; f=FALSE;
  338.                 for(l=0; (l<n->wtext.display_lines_high)&&(tl->next!=NULL); tl=tl->next )
  339.                 {
  340.                     if ((tl==n->wtext.sel_start_line)||(tl==n->wtext.sel_end_line)) f=TRUE;
  341.                     l++;
  342.                 }
  343.                 if (!f)                    // No selection markers found in display at all, check to see if whole display is selected
  344.                 {
  345.                     tl=n->wtext.sel_start_line;
  346.                     for(l=0; (l<n->wtext.display_lines_high)&&(tl->next!=NULL); tl=tl->next )
  347.                     {
  348.                         if (tl==n->wtext.display_baseline)        // If we find the display baseline in the selected text, then the whole display is selected
  349.                             f=TRUE;
  350.                         l++;
  351.                     }
  352.                 }
  353.                 if (f) TW_display_highlighted(this_dialog, this_ob);
  354.             }
  355.         }
  356.     }else{
  357.         form_alert(1,"[1][ ERROR: Cann't find text widget info. ][ Bugger ]");
  358.     }
  359.     return TRUE;
  360. }
  361.  
  362. /*
  363.     Text Widget mouse click handler
  364. */
  365. short TW_mouse_click(void)
  366. {
  367.     Elist *n;
  368.     text_line *tln_t,*tl;
  369.     short ch,cw,x,y,rx,ry,l,mb,rl_x,rl_y,lrl_x,lrl_y,e;
  370.     short f;
  371.     short pts[8];
  372.     
  373.     graf_mkstate(&lrl_x,&lrl_y,&mb,&junk);
  374.  
  375.     if (find_event(this_dialog,this_ob,&n))
  376.     {
  377.         vqt_extent(x_handle,"A",pts);
  378.         ch=abs(pts[1]-pts[7]);                // Height of one character in current font
  379.         cw=abs(pts[0]-pts[2]);                // Width of one character in current font
  380.         x=(short)((cr_mx-5/2)/cw);            // Click position in terms of characters & lines
  381.         y=(short)((cr_my-5)/ch); if (y<0) y=0;
  382.  
  383.         if (mb==0)
  384.         {
  385.             graf_mouse(M_OFF,NULL);                        // Un-display the old cursor
  386.             TW_display_cursor(this_dialog, this_ob);
  387.  
  388.             n->wtext.current_line=n->wtext.display_baseline;
  389.             for(l=0; (n->wtext.current_line->next!=NULL)&&(l<y); l++) 
  390.                 n->wtext.current_line=n->wtext.current_line->next;
  391.         
  392.             n->wtext.cursor_y=l;
  393.         
  394.             if (x>n->wtext.current_line->eof_line)
  395.                 n->wtext.current_column=n->wtext.current_line->eof_line;
  396.             else
  397.                 n->wtext.current_column=x;
  398.  
  399.             TW_display_cursor(this_dialog, this_ob);    // Display new cursor position
  400.             graf_mouse(M_ON,NULL);
  401.         }else{
  402.         /* First, erase the current selection if there is one */
  403.             if ((n->wtext.sel_start_line!=NULL)&&(n->wtext.sel_end_line!=NULL))
  404.             {
  405.                 if ((n->wtext.sel_start_line!=n->wtext.sel_end_line)||(n->wtext.sel_start_char!=n->wtext.sel_end_char))
  406.                 {
  407.                     tl=n->wtext.display_baseline; f=FALSE;
  408.                     for(l=0; (l<n->wtext.display_lines_high)&&(tl->next!=NULL); tl=tl->next )
  409.                     {
  410.                         if ((tl==n->wtext.sel_start_line)||(tl==n->wtext.sel_end_line))
  411.                             f=TRUE;
  412.                         l++;
  413.                     }
  414.                     if (!f)                                            // No selection markers found in display at all, check to see if whole display is selected
  415.                     {
  416.                         tl=n->wtext.sel_start_line;
  417.                         for(l=0; (l<n->wtext.display_lines_high)&&(tl->next!=NULL); tl=tl->next )
  418.                         {
  419.                             if (tl==n->wtext.display_baseline)        // If we find the display baseline in the selected text, then the whole display is selected
  420.                                 f=TRUE;
  421.                             l++;
  422.                         }
  423.                     }
  424.                     if (f)
  425.                     {
  426.                         graf_mouse(M_OFF,NULL);
  427.                         TW_display_highlighted(this_dialog, this_ob);
  428.                         graf_mouse(M_ON,NULL);
  429.                     }
  430.                 }
  431.             }
  432.             
  433.         /* Now, find the start line for the new selected text */
  434.             n->wtext.sel_start_line=n->wtext.display_baseline;
  435.             for(l=0; (n->wtext.sel_start_line->next!=NULL)&&(l<y); l++) 
  436.                 n->wtext.sel_start_line=n->wtext.sel_start_line->next;
  437.  
  438.             if (n->wtext.sel_start_line->next==NULL) y=l;
  439.  
  440.             if (x>n->wtext.sel_start_line->eof_line)
  441.             {
  442.                 x=n->wtext.sel_start_char=n->wtext.sel_start_line->eof_line;
  443.             }else
  444.                 n->wtext.sel_start_char=x;
  445.  
  446.             rx=x; ry=y;
  447.             do
  448.             {
  449.                 e=evnt_multi(MU_BUTTON|MU_M1,1,1,1,
  450.                                     1,lrl_x,lrl_y,1,1,
  451.                                     0,0,0,0,0,
  452.                                     messB,1,0,&rl_x,&rl_y,&mb,&junk,&junk,&junk);
  453.                 graf_mkstate(&junk,&junk,&mb,&junk);
  454.  
  455.                 if (e&MU_M1)
  456.                 {
  457.                     graf_mouse(M_OFF,NULL);
  458.                 
  459.                     if ((rx!=x)||(ry!=y))                     // Erase the current highlight
  460.                         TW_display_highlighted(this_dialog, this_ob);
  461.  
  462.                     rx=(rl_x-cr_clip.g_x-5)/cw;
  463.                     ry=(rl_y-cr_clip.g_y-5)/ch;
  464.                     if (ry<=y)                                // Can only mark forwards using this :(
  465.                     {
  466.                         ry=y;
  467.                         if (rx<x) rx=x;
  468.                     }
  469.     
  470.                     tln_t=n->wtext.display_baseline;        // Find the line containing the end marker
  471.                     for (l=0; (tln_t->next!=NULL)&&(l<ry); l++) tln_t=tln_t->next;
  472.                     n->wtext.sel_end_line=tln_t;
  473.                     
  474.                     if (tln_t->next==NULL) ry=l;
  475.                     
  476.                     if (rx>tln_t->eof_line)                    // Make sure we cann't select past eofline
  477.                     {
  478.                         rx=n->wtext.sel_end_char=tln_t->eof_line;
  479.                     }else
  480.                         n->wtext.sel_end_char=rx;
  481.  
  482.                     if ((rx!=x)||(ry!=y))                     // Display the new highlight
  483.                         TW_display_highlighted(this_dialog, this_ob);
  484.  
  485.                     graf_mouse(M_ON,NULL);
  486.                     lrl_x=rl_x; lrl_y=rl_y;
  487.                 }
  488.             } while (mb);
  489.         }
  490.     }
  491.  
  492.     return FALSE;
  493. }
  494.  
  495. /*
  496.     Copy selected text to the GEM clipboard
  497. */
  498. void TW_copy_text(short dialog, short object)
  499. {
  500.     Elist *n;
  501.     FILE *scrap_file;
  502.     text_line *tt;
  503.     text_line *tn;
  504.     char p[FMSIZE+10];
  505.     char *c, *tmp;
  506.  
  507.     if (find_event(dialog,object,&n))
  508.     {
  509.         sprintf(p,"%s%s",clipboard_path,"SCRAP.TXT");
  510.         scrap_file=fopen(p,"w");
  511.  
  512.         if (scrap_file)
  513.         {
  514.             tmp=(char*)malloc(sizeof(char)*(n->wtext.max_columns+3));
  515.             c=n->wtext.sel_start_line->the_text + n->wtext.sel_start_char;
  516.             sprintf(tmp,"%s",c);
  517.             if (n->wtext.sel_start_line==n->wtext.sel_end_line)
  518.                 tmp[n->wtext.sel_end_char - n->wtext.sel_start_char]='\0';
  519.         
  520.             fprintf(scrap_file,"%s\n",tmp);
  521.     
  522.             if (n->wtext.sel_start_line!=n->wtext.sel_end_line)
  523.             {
  524.                 tt=n->wtext.sel_start_line->next; tn=n->wtext.sel_end_line;
  525.                 while (tt!=tn)
  526.                 {
  527.                     fprintf(scrap_file,"%s\n",tt->the_text);
  528.                     tt=tt->next;
  529.                 }
  530.         
  531.                 sprintf(tmp,"%s",n->wtext.sel_end_line->the_text);
  532.                 tmp[n->wtext.sel_end_char]='\0';
  533.                 fprintf(scrap_file,"%s\n",tmp);
  534.             }
  535.             fclose(scrap_file);
  536.  
  537.             free(tmp);
  538.         }else{
  539.             form_alert(1,"[1][ ERROR: Cann't write to clipboard. ][ Bugger ]");
  540.         }
  541.     }else{
  542.         form_alert(1,"[1][ ERROR: Cann't find text widget info. ][ Bugger ]");
  543.     }
  544. }
  545.  
  546. /*
  547.     Cut selected text to the GEM clipboard
  548. */
  549. void TW_cut_text(short dialog, short object)
  550. {
  551.     Elist *n;
  552.     text_line *tl, *tln;
  553.     char *tmp,*t1,*t2;
  554.     
  555.     TW_copy_text(dialog, object);            // First, copy the text to the clipboard
  556.     
  557.     if (find_event(dialog, object,&n))
  558.     {
  559.         tmp=(char*)malloc(sizeof(char)*(n->wtext.max_columns+4));
  560.         if (n->wtext.sel_start_line!=n->wtext.sel_end_line)        // Delete text from first line
  561.         {
  562.             n->wtext.sel_start_line->the_text[n->wtext.sel_start_char]='\0';
  563.         }else{
  564.             sprintf(tmp,"%s",n->wtext.sel_start_line->the_text);
  565.             t1=n->wtext.sel_start_line->the_text + n->wtext.sel_end_char;
  566.             t2=tmp+n->wtext.sel_start_char;
  567.             sprintf(t2,"%s",t1);
  568.             sprintf(n->wtext.sel_start_line->the_text,"%s",tmp);
  569.         }
  570.         n->wtext.sel_start_line->eof_line=strlen(n->wtext.sel_start_line->the_text);
  571.         
  572.         if (n->wtext.sel_start_line!=n->wtext.sel_end_line)        // Delete whole lines that are going
  573.         {
  574.             tl=n->wtext.sel_start_line->next;
  575.             while (tl!=n->wtext.sel_end_line)
  576.             {
  577.                 tln=tl->next;
  578.                 free(tl->the_text);
  579.                 free(tl);
  580.                 tl=tln;
  581.             }
  582.             n->wtext.sel_start_line->next=n->wtext.sel_end_line;
  583.  
  584.             t1=n->wtext.sel_end_line->the_text + n->wtext.sel_end_char;    // Delete start of last line
  585.             sprintf(tmp,"%s",t1);
  586.             sprintf(n->wtext.sel_end_line->the_text,"%s",tmp);
  587.         }
  588.  
  589.         n->wtext.sel_end_line=n->wtext.sel_start_line;
  590.         n->wtext.sel_end_char=n->wtext.sel_start_char=0;
  591.         free(tmp);
  592.     }else{
  593.         form_alert(1,"[1][ ERROR: Cann't find text widget info. ][ Bugger ]");
  594.     }
  595.  
  596. }
  597.  
  598. /*
  599.     The Text Widget Keyboard Handler
  600.      The TWKH runs as an Object Layer key handler associated with text widget objects.
  601.      This copes with all the standard text editting CTRL+key combo's, those which aren't
  602.      recognised are dropped through to the Dialog Layer.
  603.      All ALT+key combos are dropped through to the Dialog Layer.
  604.  
  605.     This is pretty messy - don't laugh, at least it works.
  606. */
  607. short TW_keypress(void)
  608. {
  609.     Elist *n;
  610.     text_line *tln_t;
  611.     char c, *tmp;
  612.     short p,f;
  613.     
  614.     if (!find_event(this_dialog,this_ob,&n))
  615.     {
  616.         form_alert(1,"[1][ ERROR: Cann't find text widget info. ][ Bugger ]");
  617.         return FALSE;
  618.     }
  619.  
  620.     if (kc_shstate&K_ALT)        // Text widgets don't use ALT+key combo's
  621.         return FALSE;            //  so let the key through to Dialog Layer.
  622.  
  623.     if (kc_shstate&K_CTRL)        // Is control held down? If so, there may be a CTRL+key combo we want to know about.
  624.     {
  625.         switch (kc_key)                // Do plain cursor & special key parsing
  626.         {
  627.             case 0x2e03:            // ^C==Copy
  628.                 if ((n->wtext.sel_start_line!=n->wtext.sel_end_line)||(n->wtext.sel_start_char!=n->wtext.sel_end_char))
  629.                 {
  630.                     TW_copy_text(this_dialog, this_ob);
  631.                 }
  632.                 return TRUE;
  633.                 break;
  634.             case 0x2d18:            // ^X==Cut
  635.                 if ((n->wtext.sel_start_line!=n->wtext.sel_end_line)||(n->wtext.sel_start_char!=n->wtext.sel_end_char))
  636.                 {
  637.                     TW_cut_text(this_dialog, this_ob);
  638.                     graf_mouse(M_OFF,NULL);
  639.                     TW_display();
  640.                     graf_mouse(M_ON,NULL);
  641.                 }
  642.                 return TRUE;
  643.                 break;
  644.             
  645.         }
  646.         return FALSE;            // Allow any CTRL+key combo's that the text_widget doesn't understand fall through to the Dialog layer.
  647.     }
  648.     
  649.     switch (kc_key)                // Do plain cursor & special key parsing
  650.     {
  651.         case 0x4800:            // Cursor up
  652.             if (n->wtext.current_line->prev)
  653.             {
  654.                 if (n->wtext.cursor_y)
  655.                 {
  656.                     graf_mouse(M_OFF,NULL);
  657.                     TW_display_cursor(this_dialog, this_ob);
  658.                     n->wtext.current_line=n->wtext.current_line->prev;
  659.                     n->wtext.cursor_y--;
  660.                     if (n->wtext.current_column>n->wtext.current_line->eof_line)
  661.                         n->wtext.current_column=n->wtext.current_line->eof_line;
  662.                     TW_display_cursor(this_dialog, this_ob);
  663.                     graf_mouse(M_ON,NULL);
  664.                 }else{
  665.                     n->wtext.display_baseline=n->wtext.current_line=n->wtext.current_line->prev;
  666.                     if (n->wtext.current_column>n->wtext.current_line->eof_line)
  667.                         n->wtext.current_column=n->wtext.current_line->eof_line;
  668.                     graf_mouse(M_OFF,NULL);
  669.                     TW_display();
  670.                     graf_mouse(M_ON,NULL);
  671.                 }
  672.                                 
  673.             }
  674.             return TRUE;        // Consume
  675.             break;
  676.         case 0x4838:            // Shift+Cursor up
  677.             for (f=0; (f<n->wtext.display_lines_high)&&(n->wtext.display_baseline->prev!=NULL); f++)
  678.             {
  679.                 n->wtext.display_baseline=n->wtext.display_baseline->prev;
  680.                 n->wtext.current_line=n->wtext.current_line->prev;
  681.             }
  682.             if (n->wtext.current_column>n->wtext.current_line->eof_line)
  683.                 n->wtext.current_column=n->wtext.current_line->eof_line;
  684.             graf_mouse(M_OFF,NULL);
  685.             TW_display();
  686.             graf_mouse(M_ON,NULL);
  687.             return TRUE;        // Consume
  688.             break;
  689.         case 0x5000:            // Cursor down
  690.             if (n->wtext.current_line->next)
  691.             {
  692.                 if (n->wtext.cursor_y<n->wtext.display_lines_high-1)
  693.                 {
  694.                     graf_mouse(M_OFF,NULL);
  695.                     TW_display_cursor(this_dialog, this_ob);
  696.                     n->wtext.current_line=n->wtext.current_line->next;
  697.                     n->wtext.cursor_y++;
  698.                     if (n->wtext.current_column>n->wtext.current_line->eof_line)
  699.                         n->wtext.current_column=n->wtext.current_line->eof_line;
  700.                     TW_display_cursor(this_dialog, this_ob);
  701.                     graf_mouse(M_ON,NULL);
  702.                 }else{
  703.                     n->wtext.display_baseline=n->wtext.display_baseline->next;
  704.                     n->wtext.current_line=n->wtext.current_line->next;
  705.                     if (n->wtext.current_column>n->wtext.current_line->eof_line)
  706.                         n->wtext.current_column=n->wtext.current_line->eof_line;
  707.                     graf_mouse(M_OFF,NULL);
  708.                     TW_display();
  709.                     graf_mouse(M_ON,NULL);
  710.                 }
  711.                                     
  712.             }
  713.             return TRUE;        // Consume
  714.             break;
  715.         case 0x5032:            // Shift+Cursor down
  716.             for (f=0; (f<n->wtext.display_lines_high)&&(n->wtext.current_line->next!=NULL); f++)
  717.             {
  718.                 n->wtext.display_baseline=n->wtext.display_baseline->next;
  719.                 n->wtext.current_line=n->wtext.current_line->next;
  720.             }
  721.             if (n->wtext.current_column>n->wtext.current_line->eof_line)
  722.                 n->wtext.current_column=n->wtext.current_line->eof_line;
  723.             graf_mouse(M_OFF,NULL);
  724.             TW_display();
  725.             graf_mouse(M_ON,NULL);
  726.             return TRUE;        // Consume
  727.             break;
  728.         case 0x4d00:            // Cursor right
  729.             if (n->wtext.current_column<n->wtext.current_line->eof_line) // If we aren't at RHS, step right one character
  730.             {
  731.                 graf_mouse(M_OFF,NULL);
  732.                 TW_display_cursor(this_dialog, this_ob);
  733.                 n->wtext.current_column++;
  734.                 TW_display_cursor(this_dialog, this_ob);
  735.                 graf_mouse(M_ON,NULL);
  736.             }else{                            // Otherwise, try to go to start  of next line
  737.                 if (n->wtext.current_line->next)
  738.                 {
  739.                     if (n->wtext.cursor_y<n->wtext.display_lines_high-1)
  740.                     {
  741.                         graf_mouse(M_OFF,NULL);
  742.                         TW_display_cursor(this_dialog, this_ob);
  743.                         n->wtext.current_column=0;
  744.                         n->wtext.current_line=n->wtext.current_line->next;
  745.                         n->wtext.cursor_y++;
  746.                         TW_display_cursor(this_dialog, this_ob);
  747.                         graf_mouse(M_ON,NULL);
  748.                     }else{
  749.                         n->wtext.current_column=0;
  750.                         n->wtext.display_baseline=n->wtext.display_baseline->next;
  751.                         n->wtext.current_line=n->wtext.current_line->next;
  752.                         graf_mouse(M_OFF,NULL);
  753.                         TW_display();
  754.                         graf_mouse(M_ON,NULL);
  755.                     }
  756.                 }
  757.             }
  758.             return TRUE;        // Consume
  759.             break;
  760.         case 0x4d36:            // Shift+Cursor right
  761.             if (n->wtext.current_column<n->wtext.current_line->eof_line)
  762.             {
  763.                 n->wtext.current_column=n->wtext.current_line->eof_line;
  764.  
  765.                 graf_mouse(M_OFF,NULL);
  766.                 TW_display();
  767.                 graf_mouse(M_ON,NULL);
  768.             }
  769.             return TRUE;        // Consume
  770.             break;
  771.         case 0x4b00:            // Cursor left
  772.             if (n->wtext.current_column>0)    // If we aren't at LHS, just step left
  773.             {
  774.                 graf_mouse(M_OFF,NULL);
  775.                 TW_display_cursor(this_dialog, this_ob);
  776.                 n->wtext.current_column--;
  777.                 TW_display_cursor(this_dialog, this_ob);
  778.                 graf_mouse(M_ON,NULL);
  779.             }else{                            // Otherwise, try to go to EOF previous line
  780.                 if (n->wtext.current_line->prev)
  781.                 {
  782.                     if (n->wtext.cursor_y)
  783.                     {
  784.                         graf_mouse(M_OFF,NULL);
  785.                         TW_display_cursor(this_dialog, this_ob);
  786.                         n->wtext.current_line=n->wtext.current_line->prev;
  787.                         n->wtext.cursor_y--;
  788.                         n->wtext.current_column=n->wtext.current_line->eof_line;
  789.                         TW_display_cursor(this_dialog, this_ob);
  790.                         graf_mouse(M_ON,NULL);
  791.                     }else{
  792.                         n->wtext.display_baseline=n->wtext.current_line=n->wtext.current_line->prev;
  793.                         n->wtext.current_column=n->wtext.current_line->eof_line;
  794.                         graf_mouse(M_OFF,NULL);
  795.                         TW_display();
  796.                         graf_mouse(M_ON,NULL);
  797.                     }
  798.                 }
  799.             }
  800.             return TRUE;        // Consume
  801.             break;
  802.         case 0x4b34:            // Shift+Cursor left
  803.             if (n->wtext.current_column>0)
  804.             {
  805.                 n->wtext.current_column=0;
  806.  
  807.                 graf_mouse(M_OFF,NULL);
  808.                 TW_display();
  809.                 graf_mouse(M_ON,NULL);
  810.             }
  811.             return TRUE;        // Consume
  812.             break;
  813.         case 0x4700:            // Clear home
  814.             n->wtext.display_baseline=n->wtext.current_line=n->wtext.text;
  815.             n->wtext.current_column=0;
  816.             n->wtext.cursor_y=0;
  817.             graf_mouse(M_OFF,NULL);
  818.             TW_display();
  819.             graf_mouse(M_ON,NULL);
  820.             return TRUE;        // Consume
  821.             break;
  822.         case 0x4737:            // Shift+Clear Home == bottom of text
  823.             for (; n->wtext.current_line->next!=NULL; )
  824.             {
  825.                 n->wtext.display_baseline=n->wtext.display_baseline->next;
  826.                 n->wtext.current_line=n->wtext.current_line->next;
  827.             }
  828.             if (n->wtext.current_column>n->wtext.current_line->eof_line)
  829.                 n->wtext.current_column=n->wtext.current_line->eof_line;
  830.             graf_mouse(M_OFF,NULL);
  831.             TW_display();
  832.             graf_mouse(M_ON,NULL);
  833.             return TRUE;        // Consume
  834.             break;
  835.         case 0x1c0d:            // Enter inserts a new line
  836.             tln_t=(text_line*)malloc(sizeof(text_line));                    // Allocate a new line
  837.             tln_t->the_text=(char*)malloc(sizeof(char)*n->wtext.max_columns);    // Text store for the new line
  838.             tln_t->the_text[0]='\0';                                        // Flag the text store as initially empty
  839.             
  840.             tln_t->next=n->wtext.current_line->next;                        // Slot the new line into the lines list for this widget
  841.             n->wtext.current_line->next=tln_t;
  842.             tln_t->prev=n->wtext.current_line;
  843.             if (tln_t->next) tln_t->next->prev=tln_t;                            // If we aren't last line in the list, ensure the next line know where we are.
  844.  
  845.             if (n->wtext.current_column!=n->wtext.current_line->eof_line)    // If not at end of line, move the rest of the current line onto a new line
  846.             {
  847.                 tmp=n->wtext.current_line->the_text+(n->wtext.current_column);
  848.                 sprintf(tln_t->the_text,"%s",tmp);
  849.                 n->wtext.current_line->the_text[n->wtext.current_column]='\0';
  850.                 n->wtext.current_line->eof_line=n->wtext.current_column;
  851.             }
  852.             tln_t->eof_line=strlen(tln_t->the_text);            // Where is end of line?
  853.             
  854.             n->wtext.current_line=tln_t;                        // Current line == the new line
  855.  
  856.             n->wtext.current_column=0;                            // Always begin with cursor at start of new line
  857.             
  858.             if (n->wtext.cursor_y<n->wtext.display_lines_high-1)    // Insert a new line not at bottom of widget, move cursor down a line
  859.             {
  860.                 n->wtext.cursor_y++;
  861.             }else{                                                // Otherwise, scroll the whole widget up one line & leave cursor at same posistion
  862.                 n->wtext.display_baseline=n->wtext.display_baseline->next;
  863.             }
  864.             
  865.             graf_mouse(M_OFF,NULL);
  866.             TW_display();
  867.             graf_mouse(M_ON,NULL);
  868.             return TRUE;        // Consume
  869.             break;
  870.         case 0x0e08:            // Backspace
  871.             if (n->wtext.current_column>0)    // If we aren't at LHS, just copy over char directly to our left
  872.             {
  873.                 for(p=n->wtext.current_column-1; p<n->wtext.current_line->eof_line; p++)
  874.                     n->wtext.current_line->the_text[p]=n->wtext.current_line->the_text[p+1];
  875.  
  876.                 n->wtext.current_column--;
  877.                 n->wtext.current_line->eof_line--;
  878.                 n->wtext.current_line->the_text[n->wtext.current_line->eof_line]='\0';
  879.                 
  880.                 graf_mouse(M_OFF,NULL);
  881.                 TW_display();
  882.                 graf_mouse(M_ON,NULL);
  883.             }else{                            // Otherwise, try to go to EOF previous line
  884.                 if (n->wtext.current_line->prev)
  885.                 {
  886.                     if (n->wtext.current_line->eof_line+n->wtext.current_line->prev->eof_line < n->wtext.max_columns)
  887.                     {
  888.                         tln_t=n->wtext.current_line;
  889.                         tmp=tln_t->prev->the_text+(tln_t->prev->eof_line);        // Tag line onto the end off previous line
  890.                         sprintf(tmp,"%s",tln_t->the_text);
  891.                         n->wtext.current_column=tln_t->prev->eof_line;
  892.                         tln_t->prev->eof_line=strlen(tln_t->prev->the_text);
  893.                         tln_t->prev->the_text[tln_t->prev->eof_line]='\0';
  894.  
  895.                         tln_t->prev->next=tln_t->next;        // detatch line from list
  896.                         if (tln_t->next)
  897.                             tln_t->next->prev=tln_t->prev;
  898.                         
  899.                         if (n->wtext.display_baseline==tln_t) n->wtext.display_baseline=n->wtext.current_line->prev;
  900.                         n->wtext.current_line=n->wtext.current_line->prev;
  901.                         free(tln_t->the_text);            //free up the char array.
  902.                         free(tln_t);                    //free up the line structure
  903.                     
  904.                         if (n->wtext.cursor_y)
  905.                             n->wtext.cursor_y--;
  906.  
  907.                         graf_mouse(M_OFF,NULL);
  908.                         TW_display();
  909.                         graf_mouse(M_ON,NULL);
  910.                     }
  911.                 }
  912.             }
  913.             return TRUE;        // Consume
  914.             break;
  915.         case 0x537f:            // Delete
  916.             if (kc_shstate&(K_RSHIFT|K_LSHIFT))        // Shift Delete does delete line
  917.             {
  918.                 tln_t=n->wtext.current_line;
  919.                 if (tln_t->prev)
  920.                     tln_t->prev->next=tln_t->next;        // detatch line from list
  921.                 if (tln_t->next)
  922.                     tln_t->next->prev=tln_t->prev;
  923.                 
  924.                 if (tln_t->next)                        // If there is a next line, shift it up to be under the cursor
  925.                 {
  926.                     n->wtext.current_line=tln_t->next;
  927.                 }else{
  928.                     if (tln_t->prev)                    // Otherwise go back to previous line
  929.                     {
  930.                         n->wtext.current_line=tln_t->prev;
  931.                         if (n->wtext.cursor_y)            // If not at top of widget, move cursor up a line.
  932.                             n->wtext.cursor_y--;
  933.                     }
  934.                 }
  935.  
  936.                 if (n->wtext.display_baseline==tln_t)    // If the baseline has been deleted, change it
  937.                     n->wtext.display_baseline=n->wtext.current_line;
  938.  
  939.                 if (n->wtext.current_line!=tln_t)        // If there are still lines left, free up the deleted lines storage
  940.                 {
  941.                     free(tln_t->the_text);            //free up the char array.
  942.                     free(tln_t);                    //free up the line structure
  943.                 }else{                                    // Don't free the last line, just set it as empty otherwise there is nowhere to put characters that we type
  944.                     n->wtext.current_line->eof_line=0;
  945.                     n->wtext.current_line->the_text[0]='\0';
  946.                 }
  947.                 
  948.                 if (n->wtext.current_column>n->wtext.current_line->eof_line)
  949.                     n->wtext.current_column=n->wtext.current_line->eof_line;
  950.                     
  951.                 graf_mouse(M_OFF,NULL);
  952.                 TW_display();
  953.                 graf_mouse(M_ON,NULL);
  954.                 
  955.             }else{                                    // Delete on it's own does delete char under cursor
  956.                 if (n->wtext.current_column<n->wtext.current_line->eof_line)    // Not at end of line yet, so simply shuffle things back to erase current character.
  957.                 {
  958.                     for(p=n->wtext.current_column; p<n->wtext.current_line->eof_line; p++)
  959.                         n->wtext.current_line->the_text[p]=n->wtext.current_line->the_text[p+1];
  960.  
  961.                     n->wtext.current_line->eof_line--;
  962.                     n->wtext.current_line->the_text[n->wtext.current_line->eof_line]='\0';
  963.                 
  964.                     graf_mouse(M_OFF,NULL);
  965.                     TW_display();
  966.                     graf_mouse(M_ON,NULL);
  967.                 }else{                            // Otherwise, try to append next line to this one
  968.                     if (n->wtext.current_line->next)
  969.                     {
  970.                         if (n->wtext.current_line->eof_line+n->wtext.current_line->next->eof_line < n->wtext.max_columns)
  971.                         {
  972.                             tln_t=n->wtext.current_line->next;
  973.                             tmp=n->wtext.current_line->the_text+(n->wtext.current_line->eof_line);        // Tag next line onto the end off this line
  974.                             sprintf(tmp,"%s",tln_t->the_text);
  975.                             n->wtext.current_line->eof_line=strlen(n->wtext.current_line->the_text);
  976.                             n->wtext.current_line->the_text[n->wtext.current_line->eof_line]='\0';
  977.  
  978.                             n->wtext.current_line->next=tln_t->next;        // detatch line from list
  979.                             if (tln_t->next)
  980.                                 tln_t->next->prev=n->wtext.current_line;
  981.                         
  982.                             free(tln_t->the_text);            //free up the char array.
  983.                             free(tln_t);                    //free up the line structure
  984.                     
  985.                             graf_mouse(M_OFF,NULL);
  986.                             TW_display();
  987.                             graf_mouse(M_ON,NULL);
  988.                         }
  989.                     }
  990.                 }
  991.             }
  992.             return TRUE;
  993.             break;
  994.     }
  995.     
  996.     // If we get to here, then we must just have a plain character typed.
  997.     
  998.     c=(char)(kc_key&255);        // Convert scan code into ASCII character.
  999.     
  1000.     if (n->wtext.current_line->eof_line<n->wtext.max_columns)            // Insert character into line (if line isn't full).
  1001.     {
  1002.         if (n->wtext.current_column!=n->wtext.current_line->eof_line)    // If not at end of line, insert a space for the character
  1003.         {
  1004.             for(p=n->wtext.current_line->eof_line; p>n->wtext.current_column; p--)
  1005.                 n->wtext.current_line->the_text[p]=n->wtext.current_line->the_text[p-1];
  1006.         }
  1007.         n->wtext.current_line->the_text[n->wtext.current_column]=c;
  1008.         n->wtext.current_column++;
  1009.         n->wtext.current_line->eof_line++;
  1010.         n->wtext.current_line->the_text[n->wtext.current_line->eof_line]='\0';
  1011.         
  1012.         graf_mouse(M_OFF,NULL);
  1013.         TW_display();
  1014.         graf_mouse(M_ON,NULL);
  1015.         return TRUE;        // Consume
  1016.     }
  1017.  
  1018.     return TRUE;    // Consume
  1019. }
  1020.  
  1021.